﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Net;
using System.Threading;

namespace NetworkingLibrary.Servers.Combined
{
    public interface IUDPTCPCombinedServer : Servers.TCP.ITCPServer, Servers.UDP.IUDPServer
    {
        event Events.ClientConnectDelegate OnClientConnectTCP;
        event Events.ClientDisconnectDelegate OnClientDisconnectTCP;
        event Events.MaxConnectionsDelegate OnMaxConnectionsTCP;
        event Events.MessageReceivedDelegate OnMessageReceivedTCP;

        event Events.ClientConnectDelegate OnClientConnectUDP;
        event Events.ClientDisconnectDelegate OnClientDisconnectUDP;
        event Events.MaxConnectionsDelegate OnMaxConnectionsUDP;
        event Events.MessageReceivedDelegate OnMessageReceivedUDP;

        int TCPBufferSize
        {
            get;
            set;
        }
        int UDPBufferSize
        {
            get;
            set;
        }
        
        bool TCPInitialised
        {
            get;
        }
        bool UDPInitialised
        {
            get;
        }

        IPAddress udpMulticastAddress
        {
            get;
            set;
        }

        bool StartBoth(int TCPPort, int UDPPort, string TCPAddr, int? MaximumConnections = null, IPAddress UDPMulticastAddress = null, int UDPMulticastTTL = 0);
        bool StartBoth(int TCPPort, int UDPPort, string TCPAddr, int? TCPMaximumConnections = null, int? UDPMaximumConnections = null, IPAddress UDPMulticastAddress = null, int UDPMulticastTTL = 0);
        bool StopTCP();
        bool StopUDP();

        bool SendUDP(byte[] TheBuffer);
        bool SendToUDP(byte[] TheBuffer, Guid ToID);
        bool SendUDP(byte[] TheBuffer, Guid ExcludeID);
        bool SendUDP(Messages.MessageObject TheMessage);
        bool SendToUDP(Messages.MessageObject TheMessage, Guid ToId);
        bool SendUDP(Messages.MessageObject TheMessage, Guid ExcludeID);
    }

    public class UDPTCPCombinedServer : IUDPTCPCombinedServer
    {
        public int MaxFlowRate
        {
            get
            {
                return TheTCPServer.MaxFlowRate > TheUDPServer.MaxFlowRate ? TheTCPServer.MaxFlowRate : TheUDPServer.MaxFlowRate;
            }
            set
            {
                TheUDPServer.MaxFlowRate = TheTCPServer.MaxFlowRate = value;
            }
        }
        public DateTime LastSendTime
        {
            get
            {
                return TheTCPServer.LastSendTime > TheUDPServer.LastSendTime ? TheTCPServer.LastSendTime : TheUDPServer.LastSendTime;
            }
            set
            {
                TheUDPServer.LastSendTime = TheTCPServer.LastSendTime = value;
            }
        }
        public bool FlowControl
        {
            get
            {
                return false; //TheTCPServer
            }
        }
        
        private TCP.TCPServer TheTCPServer = new TCP.TCPServer();
        private UDP.UDPServer TheUDPServer = new UDP.UDPServer();

        public event Events.ClientConnectDelegate OnClientConnect = delegate { };
        public event Events.ClientDisconnectDelegate OnClientDisconnect = delegate { };
        public event Events.MaxConnectionsDelegate OnMaxConnections = delegate { };
        public event Events.MessageReceivedDelegate OnMessageReceived = delegate { };
        public event Events.ErrorDelegate OnError = delegate { };

        public event Events.ClientConnectDelegate OnClientConnectTCP = delegate { };
        public event Events.ClientDisconnectDelegate OnClientDisconnectTCP = delegate { };
        public event Events.MaxConnectionsDelegate OnMaxConnectionsTCP = delegate { };
        public event Events.MessageReceivedDelegate OnMessageReceivedTCP = delegate { };
        
        public event Events.ClientConnectDelegate OnClientConnectUDP = delegate { };
        public event Events.ClientDisconnectDelegate OnClientDisconnectUDP = delegate { };
        public event Events.MaxConnectionsDelegate OnMaxConnectionsUDP = delegate { };
        public event Events.MessageReceivedDelegate OnMessageReceivedUDP = delegate { };
        
        public int BufferSize
        {
            get
            {
                return TheTCPServer.BufferSize > TheUDPServer.BufferSize ? TheTCPServer.BufferSize : TheUDPServer.BufferSize;
            }
            set
            {
                TheUDPServer.BufferSize = TheTCPServer.BufferSize = value;
            }
        }
        public int TCPBufferSize
        {
            get
            {
                return TheTCPServer.BufferSize;
            }
            set
            {
                TheTCPServer.BufferSize = value;
            }
        }
        public int UDPBufferSize
        {
            get
            {
                return TheUDPServer.BufferSize;
            }
            set
            {
                TheUDPServer.BufferSize = value;
            }
        }
        
        public bool Initialised
        {
            get
            {
                return TheTCPServer.Initialised || TheUDPServer.Initialised;
            }
        }
        public bool TCPInitialised
        {
            get
            {
                return TheTCPServer.Initialised;
            }
        }
        public bool UDPInitialised
        {
            get
            {
                return TheUDPServer.Initialised;
            }
        }
        
        public bool Terminating
        {
            get
            {
                return (!TheTCPServer.Initialised || TheTCPServer.Terminating) && (!TheUDPServer.Initialised || TheUDPServer.Terminating);
            }
            set
            {
                if (value)
                {
                    TheUDPServer.Terminating = TheTCPServer.Terminating = value;
                }
            }
        }

        public int MaxConnections
        {
            get
            {
                return TheTCPServer.MaxConnections > TheUDPServer.MaxConnections ? TheTCPServer.MaxConnections : TheUDPServer.MaxConnections;
            }
            set
            {
                TheUDPServer.MaxConnections = TheTCPServer.MaxConnections = value;
            }
        }

        public Dictionary<Guid, Clients.IClient> AllClients
        {
            get
            {
                return TheTCPServer.AllClients.Concat(TheUDPServer.AllClients).ToDictionary(x => x.Key, x => x.Value as Clients.IClient);
            }
        }

        public IPAddress LocalAddress
        {
            get
            {
                return Dns.GetHostAddresses(Dns.GetHostName()).First();
            }
        }
        public IPAddress udpMulticastAddress
        {
            get;
            set;
        }

        public Utilities.UDP.UDPMessagingMode UDPMode
        {
            get
            {
                return TheUDPServer.UDPMode;
            }
            set
            {
                TheUDPServer.UDPMode = value;
            }
        }
        
        public UDPTCPCombinedServer()
        {
        }
        public UDPTCPCombinedServer(Events.MaxConnectionsDelegate OnMaxConnectionsReached, Events.MessageReceivedDelegate OnAMessageReceived)
        {
            OnMaxConnections += OnMaxConnectionsReached;
            OnMessageReceived += OnAMessageReceived;
        }

        public bool Initialise(int TCPPort, string Addr)
        {
            return Initialise(TCPPort, -1, Addr);
        }
        public bool Initialise(int TCPPort, int UDPPort, string TCPAddr)
        {
            try
            {
                bool OK = true;

                if (OK && TCPPort != -1)
                {
                    TheTCPServer.Initialise(TCPPort, TCPAddr);
                }
                if (OK && UDPPort != -1)
                {
                    OK = TheUDPServer.Initialise(UDPPort, null);
                }

                if (OK)
                {
                    TheTCPServer.BufferSize = TheUDPServer.BufferSize;
                    TheUDPServer.MaxConnections = TheTCPServer.MaxConnections;

                    TheTCPServer.OnClientConnect += new Events.ClientConnectDelegate(TheTCPServer_OnClientConnect);
                    TheTCPServer.OnClientDisconnect += new Events.ClientDisconnectDelegate(TheTCPServer_OnClientDisconnect);
                    TheTCPServer.OnError += new Events.ErrorDelegate(TheTCPServer_OnError);
                    TheTCPServer.OnMaxConnections += new Events.MaxConnectionsDelegate(TheTCPServer_OnMaxConnections);
                    TheTCPServer.OnMessageReceived += new Events.MessageReceivedDelegate(TheTCPServer_OnMessageReceived);

                    TheUDPServer.OnClientConnect += new Events.ClientConnectDelegate(TheUDPServer_OnClientConnect);
                    TheUDPServer.OnClientDisconnect += new Events.ClientDisconnectDelegate(TheUDPServer_OnClientDisconnect);
                    TheUDPServer.OnError += new Events.ErrorDelegate(TheUDPServer_OnError);
                    TheUDPServer.OnMaxConnections += new Events.MaxConnectionsDelegate(TheUDPServer_OnMaxConnections);
                    TheUDPServer.OnMessageReceived += new Events.MessageReceivedDelegate(TheUDPServer_OnMessageReceived);
                }

                return OK;
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
            return false;
        }

        private void TheUDPServer_OnMessageReceived(Events.MessageReceivedEventArgs e)
        {
            try
            {
                OnMessageReceived.Invoke(e);
                OnMessageReceivedUDP.Invoke(e);
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
        }
        private void TheUDPServer_OnMaxConnections(Events.MaxConnectionsEventArgs e)
        {
            try
            {
                OnMaxConnections.Invoke(e);
                OnMaxConnectionsUDP.Invoke(e);
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
        }
        private void TheUDPServer_OnError(Events.ErrorEventArgs e)
        {
            try
            {
                OnError.Invoke(e);
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
        }
        private void TheUDPServer_OnClientDisconnect(Events.ClientDisconnectEventArgs e)
        {
            try
            {
                OnClientDisconnect.Invoke(e);
                OnClientDisconnectUDP.Invoke(e);
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
        }
        private void TheUDPServer_OnClientConnect(Events.ClientConnectEventArgs e)
        {
            try
            {
                OnClientConnect.Invoke(e);
                OnClientConnectUDP.Invoke(e);
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
        }

        private void TheTCPServer_OnMessageReceived(Events.MessageReceivedEventArgs e)
        {
            try
            {
                OnMessageReceived.Invoke(e);
                OnMessageReceivedTCP.Invoke(e);
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
        }
        private void TheTCPServer_OnMaxConnections(Events.MaxConnectionsEventArgs e)
        {
            try
            {
                OnMaxConnections.Invoke(e);
                OnMaxConnectionsTCP.Invoke(e);
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
        }
        private void TheTCPServer_OnError(Events.ErrorEventArgs e)
        {
            try
            {
                OnError.Invoke(e);
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
        }
        private void TheTCPServer_OnClientDisconnect(Events.ClientDisconnectEventArgs e)
        {
            try
            {
                OnClientDisconnect.Invoke(e);
                OnClientDisconnectTCP.Invoke(e);
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
        }
        private void TheTCPServer_OnClientConnect(Events.ClientConnectEventArgs e)
        {
            try
            {
                OnClientConnect.Invoke(e);
                OnClientConnectTCP.Invoke(e);
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
        }

        public bool Start(int Port, string Addr, int? MaxConnections = null)
        {
            try
            {
                bool OK = Initialise(Port, -1, Addr);

                if (OK)
                {
                    OK = TheTCPServer.Start(Port, Addr, MaxConnections);
                }

                return OK;
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
            return false;
        }
        public bool Start(int Port, string Addr, int? MaximumConnections = null, IPAddress MulticastAddress = null, int MulticastTTL = 0)
        {
            try
            {
                bool OK = Initialise(-1, Port, Addr);

                if (OK)
                {
                    udpMulticastAddress = MulticastAddress;
                    OK = TheUDPServer.Start(Port, Addr, MaximumConnections, MulticastAddress, MulticastTTL);
                }

                return OK;
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
            return false;
        }
        public bool StartBoth(int TCPPort, int UDPPort, string TCPAddr, int? MaximumConnections = null, IPAddress UDPMulticastAddress = null, int UDPMulticastTTL = 0)
        {
            return StartBoth(TCPPort, UDPPort, TCPAddr, MaximumConnections, MaximumConnections, UDPMulticastAddress, UDPMulticastTTL);
        }
        public bool StartBoth(int TCPPort, int UDPPort, string TCPAddr, int? TCPMaximumConnections = null, int? UDPMaximumConnections = null, IPAddress UDPMulticastAddress = null, int UDPMulticastTTL = 0)
        {
            try
            {
                bool OK = Initialise(TCPPort, UDPPort, TCPAddr);

                if (OK)
                {
                    OK = TheTCPServer.Start(TCPPort, TCPAddr, TCPMaximumConnections);
                }
                if (OK)
                {
                    udpMulticastAddress = UDPMulticastAddress;
                    OK = TheUDPServer.Start(UDPPort, null, UDPMaximumConnections, UDPMulticastAddress, UDPMulticastTTL);
                }

                return OK;
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
            return false;
        }
        public bool Stop()
        {
            try
            {
                return TheTCPServer.Stop() && TheUDPServer.Stop();
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
            return false;
        }
        public bool StopTCP()
        {
            try
            {
                return TheTCPServer.Stop();
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
            return false;
        }
        public bool StopUDP()
        {
            try
            {
                return TheUDPServer.Stop();
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
            return false;
        }

        public bool Send(byte[] TheBuffer)
        {
            return Send(new Messages.DataMessage(TheBuffer));
        }
        public bool SendTo(byte[] TheBuffer, Guid ToID)
        {
            return SendTo(new Messages.DataMessage(TheBuffer), ToID);
        }
        public bool Send(byte[] TheBuffer, Guid ExcludeID)
        {
            return Send(new Messages.DataMessage(TheBuffer), ExcludeID);
        }
        public bool Send(Messages.MessageObject TheMessage)
        {
            return TheTCPServer.Send(TheMessage);
        }
        public bool SendTo(Messages.MessageObject TheMessage, Guid ToId)
        {
            return TheTCPServer.SendTo(TheMessage, ToId);
        }
        public bool Send(Messages.MessageObject TheMessage, Guid ExcludeID)
        {
            return TheTCPServer.Send(TheMessage, ExcludeID);
        }

        public bool SendUDP(byte[] TheBuffer)
        {
            return Send(new Messages.DataMessage(TheBuffer));
        }
        public bool SendToUDP(byte[] TheBuffer, Guid ToID)
        {
            return SendToUDP(new Messages.DataMessage(TheBuffer), ToID);
        }
        public bool SendUDP(byte[] TheBuffer, Guid ExcludeID)
        {
            return Send(new Messages.DataMessage(TheBuffer), ExcludeID);
        }
        public bool SendUDP(Messages.MessageObject TheMessage)
        {
            return TheUDPServer.Send(TheMessage);
        }
        public bool SendToUDP(Messages.MessageObject TheMessage, Guid ToId)
        {
            return TheUDPServer.SendTo(TheMessage, ToId);
        }
        public bool SendUDP(Messages.MessageObject TheMessage, Guid ExcludeID)
        {
            return TheUDPServer.Send(TheMessage, ExcludeID);
        }

    }
}
